/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2023. All rights reserved.
 *
 */

import { HosSdkVersion, HosVersionMapper } from '@ohos/hos-sdkmanager-common';
import { ApiVersion as sdkApiVersion, isEqualApiVersion } from '@ohos/sdkmanager-common';

import { findTargetProduct } from '../common/find-target-product.js';
import { CommonConst } from '../const/common-const.js';
import { ApiVersion } from '../const/sdk-const.js';
import { ProjectModel } from '../model/project/project-model.js';
import { TargetRuntimeOS } from '../options/build/module-build-profile.js';
import { ProjectBuildProfile } from '../options/build/project-build-profile.js';

import { OhosLogger } from './log/ohos-logger.js';
import ApiMeta = ProjectBuildProfile.ApiMeta;
import ApiValType = ProjectBuildProfile.ApiValType;
import ProductBuildOpt = ProjectBuildProfile.ProductBuildOpt;

import { ValidateUtil } from './validate/validate-util.js';
import { isAtomicServiceProject } from './task-util.js';
import { HvigorConfigLoader } from '@ohos/hvigor';

export class OneSdkValidator {
  private static _log = OhosLogger.getLogger('OneSdk');

  /**
   * 根据product与target下runtimeOS配置计算runtimeOS
   *
   * @param projectModel
   */
  static evlProductRuntimeOS(projectModel: ProjectModel) {
    const currentProduct: ProjectBuildProfile.ProductBuildOpt = findTargetProduct(projectModel);
    const runtimeOSs: TargetRuntimeOS[] = projectModel.getTargetRuntimeOSs();
    if (currentProduct.runtimeOS) {
      return currentProduct.runtimeOS!;
    }
    const harmonyOS = runtimeOSs.filter((item) => item.productName === currentProduct.name).find((item) => item.runtimeOS === CommonConst.HARMONY_OS);
    return harmonyOS ? CommonConst.HARMONY_OS : CommonConst.OPEN_HARMONY;
  }

  /**
   * 版本号校验集合
   *
   * @param projectModel
   */
  static apiInspection(projectModel: ProjectModel) {
    const product: ProjectBuildProfile.ProductBuildOpt = findTargetProduct(projectModel);
    const isHarmonyOS: boolean = this.evlProductRuntimeOS(projectModel) === CommonConst.HARMONY_OS;
    const isAtomicService = isAtomicServiceProject(product, projectModel);
    ValidateUtil.integrationVersionCheck(isHarmonyOS, findTargetProduct(projectModel), isAtomicService, projectModel.getProfilePath());
    const compileSdkVersion: ProjectBuildProfile.ApiMeta = <ApiMeta>projectModel.getProductApiMeta(product.name)?.compileSdkVersion;
    const compatibleSdkVersion: ProjectBuildProfile.ApiMeta = <ApiMeta>projectModel.getProductApiMeta(product.name)?.compatibleSdkVersion;
    const targetSdkVersion: ProjectBuildProfile.ApiMeta | undefined = projectModel.getProductApiMeta(product.name)?.targetSdkVersion;

    this.versionCompatibilityCheck(projectModel, isHarmonyOS, CommonConst.COMPILE_SDK_VERSION, compileSdkVersion);
    this.versionCompatibilityCheck(projectModel, isHarmonyOS, CommonConst.COMPATIBLE_SDK_VERSION, compatibleSdkVersion);
    this.versionCompatibilityCheck(projectModel, isHarmonyOS, CommonConst.TARGET_SDK_VERSION, targetSdkVersion);

    this.versionTypeCheck(projectModel, isHarmonyOS, compileSdkVersion);
    this.versionTypeCheck(projectModel, isHarmonyOS, compatibleSdkVersion);
    this.versionTypeCheck(projectModel, isHarmonyOS, targetSdkVersion);
    const compileVersion: number = compileSdkVersion.version;
    const compatibleVersion: number = compatibleSdkVersion.version;
    const targetVersion: number | undefined = targetSdkVersion?.version;
    if (compileVersion < 8) {
      this._log.printErrorExit('SDK_VERSION_UNDER_API8', [compileVersion, projectModel.getProfilePath()]);
    }

    if (isHarmonyOS && compileSdkVersion.version > 9) {
      if (compileSdkVersion.type === 0 || compatibleSdkVersion.type === 0 || (targetSdkVersion && targetSdkVersion.type === 0)) {
        const errorId = isHarmonyOS ? 'HO_API_VERSION_VALIDATE_FAILED' : 'OH_API_VERSION_VALIDATE_FAILED';
        this._log.printErrorExit(errorId, undefined, [[projectModel.getProfilePath()]]);
      }
    }

    const shouldCheckSdkVersion = [product.compileSdkVersion, product.compatibleSdkVersion, product.targetSdkVersion];

    // 分场景验证sdk版本号的类型
    if (isHarmonyOS) {
      shouldCheckSdkVersion.forEach((sdkVersion) => this.hoApiTypeCheck(sdkVersion, projectModel));
    } else {
      shouldCheckSdkVersion.forEach((sdkVersion) => this.ohApiTypeCheck(sdkVersion, projectModel));
    }
    this.compatibleVersionCheck(isHarmonyOS, compatibleSdkVersion, projectModel);

    if (isHarmonyOS && OneSdkValidator.evlApiModel(projectModel, product) === ApiModel.ONE_MSF) {
      this.msfCompare(projectModel, compileSdkVersion, compatibleSdkVersion, targetSdkVersion);
    }
    if (
      !(targetVersion
        ? compatibleVersion <= targetVersion && compatibleVersion <= compileVersion && targetVersion <= compileVersion
        : compatibleVersion <= compileVersion)
    ) {
      this._log.printErrorExit('SDKVERSION_ORDER_ERROR', [projectModel.getProfilePath()]);
    }
  }

  static compareModelVersions(v1: string, v2: string): number {
    const parts1 = v1.split('.').map((value) => {
      return Number.isNaN(parseInt(value)) ? 0 : parseInt(value) ?? 0; // 排除NaN，
    });
    const parts2 = v2.split('.').map((value) => {
      return Number.isNaN(parseInt(value)) ? 0 : parseInt(value) ?? 0;
    });

    for (let i = 0; i < 3; i++) {
      const num1 = parts1[i];
      const num2 = parts2[i];

      if (num1 < num2) return -1;
      if (num1 > num2) return 1;
    }
    return 0;
  }
  private static compatibleVersionCheck(isHarmonyOS: boolean, compatibleVersion: ApiMeta, projectModel: ProjectModel) {
    if (compatibleVersion.minor !== 0 || compatibleVersion.patch !== 0) {
      const errorId = isHarmonyOS ? 'HO_API_VERSION_VALIDATE_FAILED' : 'OH_API_VERSION_VALIDATE_FAILED';
      this._log.printErrorExit(errorId, undefined, [[projectModel.getProfilePath()]]);
    }
  }
  static ohApiTypeCheck(api: number | string | undefined, projectModel: ProjectModel) {
    const versionValidateErrorId = 'OH_API_VERSION_VALIDATE_FAILED';
    const modelVersion = HvigorConfigLoader.getInstance().getHvigorConfig()?.modelVersion;
    // modelVersion小于6.0.0才进行新校验，因为openHarmony在modelVersion6.0.0开始为string
    const needCheckModelVersion = '6.0.0';
    if (modelVersion && OneSdkValidator.compareModelVersions(modelVersion, needCheckModelVersion) >= 0) {
      if (!!api && typeof api !== 'string') {
        this._log.printErrorExit(versionValidateErrorId, undefined, [[projectModel.getProfilePath()]]);
      }
    } else {
      if (!!api && typeof api !== 'number') {
        this._log.printErrorExit(versionValidateErrorId, undefined, [[projectModel.getProfilePath()]]);
      }
    }
  }

  static hoApiTypeCheck(api: number | string | undefined, projectModel: ProjectModel) {
    const versionValidateErrorId = 'HO_API_VERSION_VALIDATE_FAILED';
    if (!!api && typeof api === 'number') {
      this._log.printErrorExit(versionValidateErrorId, undefined, [[projectModel.getProfilePath()]]);
    }
  }

  private static versionTypeCheck(projectModel: ProjectModel, isHarmonyOS: boolean, meta?: ApiMeta) {
    if (!isHarmonyOS && meta?.type === ApiValType.NUM) {
      return;
    }
    if (!meta) {
      return;
    }
    const version: number = meta.version;
    if (meta.type === ApiValType.NUM ? version > ApiVersion.API_VERSION_9 : version < ApiVersion.API_VERSION_9) {
      const type: string = meta.type === ApiValType.NUM ? 'character string' : 'number';
      const errorId = isHarmonyOS ? 'HO_API_VERSION_VALIDATE_FAILED' : 'OH_API_VERSION_VALIDATE_FAILED';
      this._log.printErrorExit(errorId, undefined, [[projectModel.getProfilePath()]]);
    }
  }

  /**
   * 调用sdk相关接口校验harmonyOS sdkVersion是否符合配置规格 如4.0.0(10)
   *
   * @param projectModel  工程对象
   * @param isHarmonyOS  是否为HarmonyOS工程
   * @param meta  经过处理的sdkVersion信息
   * @param curVersionName  当前校验的sdkVersion名称
   * @private
   */
  private static versionCompatibilityCheck(projectModel: ProjectModel, isHarmonyOS: boolean, curVersionName: string, meta?: ApiMeta) {
    // 如果该工程不是harmonyOS工程或传入的sdkVersion是数字则不走该校验
    if (!isHarmonyOS || meta?.type === ApiValType.NUM) {
      return;
    }
    if (!meta) {
      return;
    }

    // sdk相关接口 通过sdkVersion前缀获取对应的hosSdkVersion 如通过4.0.0获取4.0.0(10)
    const hosSdkVersion = HosVersionMapper.INSTANCE.transferVersionIntoHosVersion(meta.api);

    // 如果sdkVersion不存在说明platform version不支持当前场景下的配套规格
    if (!hosSdkVersion) {
      this._log.printErrorExit(
        'SDKMANAGER_NOT_SUPPORT_THIS_API',
        [curVersionName, meta.api, meta.version, projectModel.getProfilePath()],
      );
    } else if (!this.isBaseApiEqual(hosSdkVersion, meta)) {
      // 如果sdkVersion存在但是不符合SDK的配套规格则报错
      this._log.printErrorExit('SDKMANAGER_APIVERSION_AND_PLATFORM_NOT_MATCH', [meta.api, meta.version, curVersionName, projectModel.getProfilePath()]);
    }
  }

  /**
   * 如果获取到了hosSdkVersion则需要判断baseApi是否相等 如果为错误的配置如3.1.0(10)则返回false
   *
   * @param hosSdkVersion 通过sdk提供的接口获取到的hosSdkVersion信息
   * @param version  apiVersion信息，如4.0.0(10)中的10
   * @private
   */
  private static isBaseApiEqual(hosSdkVersion: HosSdkVersion, version: ApiMeta) {
    const formatVersion = new sdkApiVersion(version.fullVersion);
    return isEqualApiVersion(hosSdkVersion.getFullBaseApi(), formatVersion);
  }

  /**
   * 校验MSF版本
   * compileSdkVersion >= targetSdkVersion >= compatibleSdkVersion
   *
   * @param projectModel
   * @param compileSdkVersion
   * @param compatibleSdkVersion
   * @param targetSdkVersion
   * @private
   */
  private static msfCompare(projectModel: ProjectModel, compileSdkVersion: ApiMeta, compatibleSdkVersion: ApiMeta, targetSdkVersion: ApiMeta | undefined) {
    let compare: number = OneSdkValidator.compareMsf(compileSdkVersion.api, compatibleSdkVersion.api, projectModel);
    if (compare < 0) {
      this._log.printErrorExit('SDKVERSION_ORDER_ERROR', [projectModel.getProfilePath()]);
    }
    if (!targetSdkVersion) {
      return;
    }
    compare = OneSdkValidator.compareMsf(compileSdkVersion.api, targetSdkVersion.api, projectModel);
    if (compare < 0) {
      this._log.printErrorExit('SDKVERSION_ORDER_ERROR', [projectModel.getProfilePath()]);
    }
    compare = OneSdkValidator.compareMsf(targetSdkVersion.api, compatibleSdkVersion.api, projectModel);
    if (compare < 0) {
      this._log.printErrorExit('SDKVERSION_ORDER_ERROR', [projectModel.getProfilePath()]);
    }
  }

  /**
   * 检查OpenHarmony product下版本号是否大于等于10
   *
   * @param version
   * @private
   */
  static ohosProductApiValid(version: number | string | undefined): boolean {
    return !!version && typeof version === 'number' && version < ApiVersion.API_VERSION_10;
  }

  /**
   * 返回 1 version0 > version1
   * 返回 -1 version0 < version1
   * 返回 0 version0 === version1
   *
   * @param version0
   * @param version1
   * @param projectModel
   */
  static compareMsf(version0: string, version1: string, projectModel: ProjectModel) {
    const a: { s: number; f: number; m: number } = OneSdkValidator.parseMsf(version0, projectModel);
    const b: { s: number; f: number; m: number } = OneSdkValidator.parseMsf(version1, projectModel);
    if (a.m !== b.m) {
      return a.m > b.m ? 1 : -1;
    }
    if (a.s !== b.s) {
      return a.s > b.s ? 1 : -1;
    }
    if (a.f !== b.f) {
      return a.f > b.f ? 1 : -1;
    }
    return 0;
  }

  /**
   * 解析M.S.F版本API
   *
   * @param version
   * @param projectModel
   * @private
   */
  private static parseMsf(version: string, projectModel: ProjectModel) {
    const vs: string[] = version.split('.');
    if (vs.length !== 3) {
      this._log.printErrorExit('HO_API_VERSION_VALIDATE_FAILED', undefined, [[projectModel.getProfilePath()]]);
    }
    const m: number = parseInt(vs[0]);
    const s: number = parseInt(vs[1]);
    const f: number = parseInt(vs[2]);
    return { m: m, s: s, f: f };
  }

  /**
   * 判断API版本的类型
   * ONE_MSF one sdk并且版本写法为 M.S.F(API)
   * ONE_NUM one sdk并且版本写法为数字 10
   * ORIGINAL 老版本写法, API声明到app下并且版本写法为数字 9
   *
   * @param projectModel
   * @param product
   */
  static evlApiModel(projectModel: ProjectModel, product: ProductBuildOpt): ApiModel {
    const compileSdkVersion: ProjectBuildProfile.ApiMeta = <ApiMeta>projectModel.getProductApiMeta(product.name)?.compileSdkVersion;
    const compatibleSdkVersion: ProjectBuildProfile.ApiMeta = <ApiMeta>projectModel.getProductApiMeta(product.name)?.compatibleSdkVersion;
    if (compileSdkVersion.type === 1 && compatibleSdkVersion.type === 1) {
      return ApiModel.ONE_MSF;
    }
    if (compileSdkVersion.type === 0 && compatibleSdkVersion.type === 0) {
      return ApiModel.ONE_NUM;
    }
    return ApiModel.ORIGINAL;
  }
}

export enum ApiModel {
  ORIGINAL = 0,
  ONE_MSF = 1,
  ONE_NUM = 2,
  UNDEFINED = 4,
}
